﻿using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TiaoTiaoCode.AutoUpdate.Common.Dtos;
using TiaoTiaoCode.AutoUpdate.Common.Exceptions;
using TiaoTiaoCode.AutoUpdate.Common.Requests;
using TiaoTiaoCode.AutoUpdate.DbCore.Entities;
using TiaoTiaoCode.AutoUpdate.ServiceCore.IServices;
using TiaoTiaoCode.BasicDbCore;
using TiaoTiaoCode.Common.Extensions;
using TiaoTiaoCode.Common.Helpers;
using TiaoTiaoCode.NLogger;

namespace TiaoTiaoCode.AutoUpdate.ServiceCore.Services
{
    /// <summary>
    /// 主程序版本信息服务
    /// </summary>
    public class MainProgramVersionService : Repository<MainProgramVersion>, IMainProgramVersionService
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public MainProgramVersionService()
        {

        }

        /// <summary>
        /// 创建更新程序
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<List<MainProgramView>> CreateMainProgramInfo(MainProgramCreateDto dto)
        {
            var entity = new MainProgramVersion
            {
                ProgramName = dto.ProgramName,
                Catalog = dto.FilePath,
                Major = 0,
                Version = 0,
                MinorNumber = 0,
                BuildNumber = 0,
                DelFlag = "0"
            };

            if (!Directory.Exists(dto.FilePath))
                Directory.CreateDirectory(dto.FilePath);

            await InsertAsync(entity);

            return await QueryVersion(dto.ProgramName);
        }

        /// <summary>
        /// 更新程序版本信息
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<List<MainProgramView>> UpdateMainProgramInfo(MainProgramUpdateDto dto)
        {
            var entity = await base.GetFirstAsync(t => t.Id == dto.Id) ?? throw new CustomException($"未找到记录【{dto.Id}】");
            entity.ProgramName = dto.ProgramName;
            entity.Catalog = dto.FilePath;
            entity.Major = dto.Major;
            entity.Version = dto.Version;
            entity.MinorNumber = dto.MinorNumber;
            entity.BuildNumber = dto.BuildNumber;
            entity.CustomVersion = dto.CustomVersion;
            entity.CreateDate = dto.CreateDate;
            entity.UpdateDate = DateTime.Now;
            entity.IsPublish = dto.IsPublish;
            entity.DelFlag = "0";

            await UpdateAsync(entity);

            return await QueryVersion(dto.ProgramName);
        }

        /// <summary>
        /// 扫描程序文件版本信息
        /// </summary>
        /// <param name="mainProgramName"></param>
        /// <returns></returns>
        public async Task<List<MainProgramFileDetailView>> ScanMainProgramFiles(string mainProgramName)
        {
            // 最后一个版本信息
            var mp = await Context.Queryable<MainProgramVersion>()
                            .Where(t => t.ProgramName == mainProgramName && t.DelFlag == "0")
                            .OrderByDescending(t => t.CreateDate)
                            .FirstAsync() ?? throw new CustomException("未找到程序信息");
            var catalog = mp.Catalog;

            // 文件扫描
            var filePaths = Directory.GetFiles(catalog, "*.*", SearchOption.AllDirectories);

            var result = new List<MainProgramFileDetailView>();

            foreach (var filePath in filePaths)
            {
                var fileInfo = new FileInfo(filePath);

                // 文件名
                var fileName = fileInfo.Name;

                // 最后修改时间
                var lastTime = fileInfo.LastWriteTime;

                var fv = FileVersionInfo.GetVersionInfo(filePath);
                var fileVersion = fv == null ? string.Empty : fv.FileVersion;
                fileVersion = fileVersion.TiaoTiaoIsNullOrEmpty() ? "0.0.0.0" : fileVersion;

                var relativePath = Path.GetDirectoryName(filePath).Replace(catalog, string.Empty);
                result.Add(new MainProgramFileDetailView(fileName, lastTime, relativePath, fileVersion));
            }

            // 比对上次文件变化
            var lastFileInfos = await Context.Queryable<ProgramFileDetail>().Where(t => t.MainProgramVersionId == mp.Id).ToListAsync();
            if (lastFileInfos.TiaoTiaoNoData())
            {
                // 首次上传 直接写入文件数据
                result.ForEach(t => t.IsNeedUpdate = true);
            }
            else
            {
                // 有上次记录，则比对文件版本号，变化的做记录
                lastFileInfos.ForEach(last =>
                {
                    var cur = result.FirstOrDefault(t => t.FileName == last.FileName && t.FilePath == last.Catalog);
                    // 文件被删除
                    if (cur == null)
                    {
                        // TODO 以后加删除文件处理
                        return;
                    }

                    // 比对
                    var b = cur.FileVersion.TiaoTiaoEqualsDotNetVersion(last.Major, last.Version, last.MinorNumber, last.BuildNumber, last.CustomVersion);

                    if (cur.FileVersion.TiaoTiaoEqualsDotNetVersion(0, 0, 0, 0, null))
                    {
                        if (last.UpdateDate == null)
                            b = false;
                        else
                            b = cur.UpdateTime.TiaoTiaoToNormal() == last.UpdateDate.TiaoTiaoToNormal();
                    }
                    cur.IsNeedUpdate = !b;
                });
            }

            // 没有需要更新的文件，不产生版本
            if (result.All(t => !t.IsNeedUpdate)) return default!;

            await AddVersionInfoToDb(mp, result);

            return result;
        }

        /// <summary>
        /// 获取更新文件列表
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public async Task<List<ProgrameFileDetailView>> ProgramFileDetails(ProgramUpdateInfoRequest req)
        {
            var query = Context.Queryable<MainProgramVersion>()
                            .Includes(t => t.ProgramFileDetails)
                            .Where(t => t.ProgramName == req.ProgramName && t.DelFlag == "0");

            var versionDto = req.CurVersion.TiaoTiaoParseDotNetVersion();

            var curMain = await query.Clone().Where(t => t.Major == versionDto.Major && t.Version == versionDto.Version
                                    && t.MinorNumber == versionDto.MinorNumber && t.BuildNumber == versionDto.BuildNumber)
                                .WhereIF(versionDto.CustomVersion.TiaoTiaoIsNotNullOrEmpty(), t => t.CustomVersion != null && t.CustomVersion == versionDto.CustomVersion)
                                .FirstAsync();

            var mains = await query.Clone().Where(t => t.CreateDate >= curMain.CreateDate && t.IsPublish == true
                                                 && !(t.Major == curMain.Major && t.Version == curMain.Version && t.MinorNumber == curMain.MinorNumber && t.BuildNumber == curMain.BuildNumber)
                                                 )
                                            .WhereIF(versionDto.CustomVersion.TiaoTiaoIsNotNullOrEmpty(), t => t.CustomVersion == null || t.CustomVersion != curMain.CustomVersion)
                                            .OrderBy(t => t.CreateDate).ToListAsync();

            var files = mains.Where(t => t.ProgramFileDetails != null)
                            .Select(t => t.ProgramFileDetails.Where(x => x.IsNeedUpdate))
                            .TiaoTiaoToFloor()
                            .OrderBy(t => t.CreateDate)
                            .AsEnumerable();
            files = files.TiaoTiaoDistinctBy(t => t.FileName);

            return files.Select(t => new ProgrameFileDetailView
            {
                Id = t.Id,
                FileName = t.FileName,
                Catalog = t.Catalog,
                Major = t.Major,
                Version = t.Version,
                MinorNumber = t.MinorNumber,
                BuildNumber = t.BuildNumber,
                CustomVersion = t.CustomVersion
            }).ToList();
        }

        /// <summary>
        /// 检测当前程序是否有更新版本
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public async Task<UpgradeInfoDto> CheckHasUpgrade(ProgramUpdateInfoRequest req)
        {
            var query = Context.Queryable<MainProgramVersion>()
                           .Includes(t => t.ProgramFileDetails)
                           .Where(t => t.ProgramName == req.ProgramName && t.DelFlag == "0");

            var versionDto = req.CurVersion.TiaoTiaoParseDotNetVersion();

            var curMain = await query.Clone().Where(t => t.Major == versionDto.Major && t.Version == versionDto.Version
                                    && t.MinorNumber == versionDto.MinorNumber && t.BuildNumber == versionDto.BuildNumber)
                                .WhereIF(versionDto.CustomVersion.TiaoTiaoIsNotNullOrEmpty(), t => t.CustomVersion == t.CustomVersion)
                                .FirstAsync();

            var mains = await query.Clone().Where(t => t.CreateDate >= curMain.CreateDate && t.IsPublish == true).OrderBy(t => t.CreateDate).ToListAsync();

            var has = mains.TiaoTiaoHasData();

            if (!has) return new UpgradeInfoDto { HasUpgrade = false };

            var newest = mains.Last();
            var version = $"{newest.Major}.{newest.Version}.{newest.MinorNumber}.{newest.BuildNumber}{(newest.CustomVersion.TiaoTiaoIsNullOrEmpty() ? string.Empty : $".{newest.CustomVersion}")}";
            return new UpgradeInfoDto { HasUpgrade = true, CurVersion = req.CurVersion, NewestVersion = version };
        }

        /// <summary>
        /// 获取应用程序维护列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<MainProgramView>> MainProgramAppList()
        {
            var data = await base.GetListAsync(t => t.Major == 0 && t.Version == 0 && t.MinorNumber == 0 && t.BuildNumber == 0);

            return data.Select(t => new MainProgramView
            {
                Id = t.Id,
                ProgramName = t.ProgramName,
                FilePath = t.Catalog,
                CreateDate = t.CreateDate,
                Major = t.Major,
                Version = t.Version,
                MinorNumber = t.MinorNumber,
                BuildNumber = t.BuildNumber,
                CustomVersion = t.CustomVersion,
                UpdateDate = t.UpdateDate,
                IsPublish = t.IsPublish,
                Comment = t.Comment
            }).ToList();
        }

        /// <summary>
        /// 发布程序版本
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<List<MainProgramView>> PublishProgram(List<MainProgramView> items)
        {
            try
            {
                Context.Ado.BeginTran();

                foreach (var item in items)
                {
                    var entity = await base.GetFirstAsync(t => t.Id == item.Id) ?? throw new CustomException($"未找到版本记录【{item.Id}】");

                    entity.IsPublish = item.IsPublish;
                    entity.Comment = item.Comment;
                    entity.UpdateDate = DateTime.Now;

                    await base.UpdateAsync(entity);
                }

                Context.Ado.CommitTran();

                if (items.Select(t => t.ProgramName).Distinct().Count() == 1)
                    return await QueryVersion(items.First().ProgramName);
                else
                    return await QueryVersion();
            }
            catch (Exception ex)
            {
                Context.Ado.RollbackTran();
                TiaoTiaoNLogger.LogError($"调整程序版本发布异常：{ex.Message}", ex);

                throw new CustomException($"调整程序版本发布异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 形成版本信息数据
        /// </summary>
        /// <param name="oldMp"></param>
        /// <param name="dtos"></param>
        /// <returns></returns>
        async Task AddVersionInfoToDb(MainProgramVersion oldMp, List<MainProgramFileDetailView> dtos)
        {
            try
            {
                Context.Ado.BeginTran();

                var newMp = oldMp.TiaoTiaoDeepClone();
                newMp.Id = SnowFlakeSingle.instance.NextId().ToString();
                newMp.CreateDate = DateTime.Now;

                var now = DateTime.Now;
                newMp.MinorNumber = now.TiaoTiaoMinorNumber();
                newMp.BuildNumber = now.TiaoTiaoBuilerNumber();
                newMp.IsPublish = false;

                await Context.Insertable(newMp).ExecuteCommandAsync();

                var entities = dtos.Select(t => new ProgramFileDetail(newMp.Id, t.FileName)
                {
                    Catalog = t.FilePath,
                    Major = t.Major,
                    Version = t.Version,
                    MinorNumber = t.MinorNumber,
                    BuildNumber = t.BuildNumber,
                    CustomVersion = t.CustomVersion,
                    IsNeedUpdate = t.IsNeedUpdate,
                    UpdateDate = t.UpdateTime
                }).ToList();

                await Context.Insertable(entities).ExecuteCommandAsync();

                Context.Ado.CommitTran();
            }
            catch (Exception ex)
            {
                Context.Ado.RollbackTran();
                TiaoTiaoNLogger.LogError($"形成版本数据异常,{ex.Message},", ex);
                throw new CustomException("801", $"形成版本数据异常,{ex.Message}");
            }
        }

        /// <summary>
        /// 查询所有未删除版本信息
        /// </summary>
        /// <returns></returns>
        public async Task<List<MainProgramView>> QueryVersion(string? programName = null)
        {
            return await Context.Queryable<MainProgramVersion>()
                        .Where(t => t.DelFlag == "0"
                                && !(t.Major == 0 && t.Version == 0 && t.MinorNumber == 0 && t.BuildNumber == 0))
                        .WhereIF(programName.TiaoTiaoIsNotNullOrEmpty(), t => t.ProgramName == programName)
                        .OrderByDescending(t => t.CreateDate)
                        .Select(t => new MainProgramView
                        {
                            Id = t.Id,
                            ProgramName = t.ProgramName,
                            FilePath = t.Catalog,
                            CreateDate = t.CreateDate,
                            Major = t.Major,
                            Version = t.Version,
                            MinorNumber = t.MinorNumber,
                            BuildNumber = t.BuildNumber,
                            CustomVersion = t.CustomVersion,
                            UpdateDate = t.UpdateDate,
                            IsPublish = t.IsPublish,
                            Comment = t.Comment
                        }).ToListAsync();
        }

        /// <summary>
        /// 升级包更新服务器程序
        /// </summary>
        /// <param name="programName"></param>
        /// <param name="fileTempName"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task UploadZipFile(string programName, string fileTempName)
        {
            var main = await base.GetFirstAsync(t => t.ProgramName == programName) ?? throw new CustomException("未找到应用程序配置");

            if (!File.Exists(fileTempName)) throw new CustomException("上传的更新包丢失");

            TiaoTiaoZip.UnZip(fileTempName, main.Catalog!);

            File.Delete(fileTempName);
        }
    }
}
